// function demo(x:number,y:number):number{
//     return x + y + 'xyz'
// }

// let result = demo(1,2)
// console.log(result);

// let str1: string // TS官方推荐的写法
// str1 = new String('123')  // 报错 不能将'String' 分配给类型'string'


// let str2: String


let a: unknown  // a为未知类型 不知道后面的人会如何用它 unknown
a = 99
a = false
a = 'hello'

console.log('a',a)

let x: string
// x = a 这会直接报错 因为它不知道a到底是啥
// 应该这样-方式1  --typeof
// if(typeof a ==='string'){
//     x = a
// }

// 方式--2 --断言
// x = a as string

// 方式3 泛型
x = <string>a


// never
let a1: never  // a1这辈子都不能翻身了
// a1 = 9



// never 函数说白了它里面就是不能结束，不能正常结束
// 限制函数的返回值---是及其特殊的函数或者死循环的函数
// function demo():never{
    // throw new Error('程序运行异常')  //--到这里会结束函数的调用
    // return 666
// }
// function demo():never{
//     demo() // 死循环
// }

// let x1 = demo()
// console.log(x1);

// 这里是never主动推断出来的
let a3: string
a3 = 'hello'
// a3 = 9
if(typeof a3 === 'string'){
    console.log(a3.toUpperCase());
}else{
    console.log(a3); // 没有任何值能走到第七行
}

// void
function logMessage(msg:string):void{
	console.log(msg)
}
logMessage('你好')

let a4: object //
let b:Object

a = {}
a = {name:'tom'}
a = [1,3,5,7,9]
a = function(){}
class Person{}
a = new Person()


// 索引签名
let person:{
    name: string
    age?: number
    // 索引签名，这里的key只是个形式，你写其他的也行，比如index,qwe
    [key:string]:any  
}

// 后面可以无限加
person = {name:'tom',age:18,gender:'男',city:'北京'}

// 函数类型
// 箭头函数右边限制的是 返回值
// TypeScript 中的 => 在函数类型声明时表示==函数类型,==描述其==参数类型==和返回类型。
let count2: (a:number,b:number) => number
count2 = function(a,b){
    return a+b
}

// 定义数组类型--方式1
let arr: string[]
// 方式2
let arr2: Array<number>
arr = ['a','b']
arr2 = [1,2,3,4]

// 枚举--默认自增
// enum Direction {
//     Up,
//     Down,
//     Left,
//     Right
// }

enum Direction {
    Up = 5,
    Down = 12,
    Left,
    Right
}

console.log('Direction',Direction);

function walk(data:Direction){
    // console.log('data',data);
    // console.log('Direction.Up',Direction.Up);   // 0
    // #region
    // #endregion
    if(data === Direction.Up){
        console.log("向上");
    }else if(data === Direction.Down){
        console.log("向下");
    }else if(data === Direction.Left){
        console.log("向左");
    }else if(data === Direction.Right){
        console.log("向右");
    }
}

// walk('up')  原来这就是枚举的作用--因为这个不行
walk(Direction.Down)  // 这个可以

// 这里加了个 const 编译时内联
const enum Direction2 {
    Up,
    Down,
    Left,
    Right
}
console.log(Direction2.Up);

// type 基本用法
type num = number
let price: num
price = 100

// type--联合类型
type Status = number | string
type Gender = '男' | '女'

function printstatus(status:Status){
    console.log(status);
}
printstatus(404)
printstatus('200')
printstatus('501')

function logGender(str:Gender){
    console.log(str);
}
logGender('男')
logGender('女')
// logGender('啊哈哈') 报错

// type---交叉类型
type Area = {
    height: number;
    width: number
}

type Address = {
    num:number
    cell:number
    room:string
}
type House = Area & Address
const house:House = {
    height: 100,
    width: 100,
    num:3,
    cell:4,
    room:'702'
}

// type Demo = number & string
// let x:Demo

// 解读 类型是个不带参数的函数 类型是 void，函数的返回值是void
type LogFunc = () => void

// LogFunc = 999
const f1:LogFunc = ()=>{
    // 不写或者 return undefined 都不报错
    // return undefined
}
// 没有学type之前的写法是这样的
function LogFunc():void{

}


// f2 和 f3是一样的意思
const f2:LogFunc = ()=> 666
const f3:LogFunc = ()=> { return 666 }

// 为啥ts在这里不会严格要求函数


// 类
class Person2 {
    public name: string
    public age:number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
    public speak(){
        console.log(`我叫:${this.name},今年${this.age}岁`);
        
    }
}
class Student extends Person2 {
    public grade:string
    constructor(name:string,age:number,grade:string){
        super(name,age)
        this.grade = grade
    }
    public study(){ // 这是子类内部
        console.log(`${this.name}正在努力学习中.......`); // 这就是类的内部
    }
    override speak(){
        console.log(`我是学生,我叫:${this.name},今年${this.age}岁`);
    }
}
const s1 = new Student('李同学',16,'三年级')
console.log(s1.study(),s1.speak());

// 简写前
class Person3 {
    public readonly name: string
    public readonly age: number
    constructor(name: string,age: number){
        this.name = name
        this.age = age
    }
}

// 简写后
// class Person4 {
//     constructor(
//         public name: string,
//         public age: number){
//     }
// }

class Person4 {
    constructor(
        protected name: string,
        protected age: number
    ){ 
        // 谁调用就指向谁
        console.log('this',this) // Person4 {name: 'tom', age: 18},Student2 {name: 'tom2', age: 12}
    }

    protected getDetails(){
        return `我叫1111: ${this.name},年龄是:${this.age}`
    }
    introduce(){
        console.log(this.getDetails());
    }
}

let p2 = new Person4('tom',18)
// p2.age
// p2.name
// p2.getDetails
// 加了protected，引用这些属性就会报错

p2.introduce()

class Student2 extends Person4 {
    // 这里写不写都可以
    // constructor(name:string,age:number){
    //     super(name,age)
    // }
    study(){
        this.getDetails()
        console.log(`${this.name}正在努力学习`);
    }
}
const s2 = new Student2('tom2',12)
s2.study()


// 抽象类
abstract class Package {
    // 构造方法
    constructor(public weight:number){}
    // 抽象方法
    // abstract calculate(x:number,y:string):void
    abstract calculate():number

    // 具体方法
    printPackage(){
        console.log(`包裹重量为:${this.weight}kg,运费为:${this.calculate()}元`);
    }
    sayHello(){}
}

// const p1 = new Package(100)
class StandardPackage extends Package {
    constructor(
        weight:number,
        public unitPrice:number
    ){
        super(weight) // 调用父类的构造函数，
        super.sayHello() // 调用父类的方法
    }

    calculate(): number {
        return this.weight * this.unitPrice
    }
}

const s4= new StandardPackage(10,5)
s4.printPackage()


// 接口---interface
interface PersonInterface {
    name: string;
    age: number;
    speak(n: number):void
}
// 定义一个类Person，实现 PersonInterface 接口
class Person5 implements PersonInterface {
    constructor(
        public name: string,
        public age: number
    ) {}

    speak(n: number):void {
        for(let i = 0; i < n; i++){
            console.log(`你好，我叫${this.name}，我的年龄是${this.age}`);
        }
    }
}
const p1 = new Person5('tom',18);
p1.speak(3)


// 接口
interface UserInterface { // 定义对象结构
    name: string
    readonly gender: string
    age?: number
    // 下面的run是一个方法(函数)，它接受一个参数n,参数类型是number,这个函数没有返回值，返回的类型是空 void
    run:(n:number) => void
}

const user:UserInterface = {
    name:'张三',
    gender:'男',
    age:28,
    run(n){
        console.log(`奔跑了${n}米`);
    }
}

// 定义函数结构
interface CountInterface {
    // 该函数接受两个参数，分别是 a 和 b，并且这两个参数都是 number 类型。来限制函数
    (a:number, b: number):number
}
const count: CountInterface = (x,y) => {
    return x + y
}

// 接口之间的继承
interface PersonInterface2 {
    name: string // 年龄
    age: number // 姓名
}

interface StudentInterface extends PersonInterface2 {
    grade: string // 年级
}

const stu2: StudentInterface =  {
    name:'张三',
    age: 18,
    grade: '三年级',
    // time:'' 报错
}

// 接口的自动合并---也就是重复定义
interface PersonInterface3 {
    name: string
    age: number
} 

interface PersonInterface3 {
    gender: string
}

// let a6 = 1
// let a6 = 2

// interface 和 type 的区别
interface PersonInterface4 {
    name: string;
    age: number;
    speak(): void
}

// type PersonType =  {
//     name: string;
//     age: number;
//     speak(): void
// }

let p6: PersonInterface4 = {
    name:'tom',
    age:18,
    speak(){
        console.log(this.name);
    }
}

// 接口可以继承和合并
interface PersonInterface4 {
    name: string
    age: number
}
interface PersonInterface4 {
    speak:()=> void
}
interface StudentInterface2 extends PersonInterface4 {
    grade: string
}

const student2: StudentInterface2 = {
    name:'山东黄金',
    age:18,
    grade:'但是',
    speak:()=>{}
}

type PersonInterface5 = {
    name: string;
    age: number;
} & { speak:()=> void }
// 相同点


// 不同点

// 泛型
// function logData<T>(data:T){
//     console.log(data);
// }
// logData<number>(100)

function logData<T,U>(data1:T,data2:U){
    return Date.now() % 2 ? console.log(data1) : console.log(data2)
}

logData<number,boolean>(100,true)
logData<string,number>('100',963)

// 泛型接口
interface PersonInterface6<T> {
    name: string,
    age: number,
    extraInfo:T
}

let p7:PersonInterface6<number> = {
    name:'tom',
    age:18,
    extraInfo:250
}

// 高级一点的泛型
type JobInfo = {
    title: string;
    company: string
}

let p:PersonInterface6<JobInfo> = {
    name:'凯杰',
    age:18,
    extraInfo:{
        title:'高级工程师',
        company:'发发发科技有限公司'
    }
}

// 泛型类
class Person6<T>{
    constructor(
        public name: string,
        public age: number,
        public extraInfo:T
    ){}

    speak(){
        console.log(`我叫${this.name}今年${this.age}岁`);
        console.log(this.extraInfo);
    }
}

// 测试代码
type JobInfo2 = {
    title: string;
    company: string
}

// const p8 = new Person6<number>('你好',18,60)
const p8 = new Person6<JobInfo2>('你好',18,{title:'hehe',company:'147'})

// 类型声明文件.d.ts
// import {add,mul} from './demo.js'
// console.log(add(1,2))
// console.log(mul(3,4))

// 类型文件的主要作用是为现有的js代码提供类型信息，
// 使得ts能够在使用这些js库或者模块时，进行类型检查和提示

/* 
Demo函数会在Person类定义时执行
参数说明：
    ○ target参数是被装饰的类，即：Person
*/
// 装饰器
function Demo2(target:Function){
    console.log('target',target);
}
@Demo2
class Person7 {
    public name:string
    public age:number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
}

function CustomString(target:Function){
    target.prototype.toString = function(){
        console.log('this',this); // 实例P9
        return JSON.stringify(this) // 这里不要填target
    }
    // Object.seal(target.prototype)
}
@CustomString
class Person8 {
    name: string
    age: number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
}

const p9 = new Person8('张三',20)
console.log(p9.toString());
interface Person8 {
    x:number
}
// @ts-ignore  这是忽略检查的意思
Person8.prototype.x = 99
console.log(p9.x);

// **类装饰器有返回值**：若类装饰器返回一个新的类，那这个新类将**替换**掉被装饰的类。
// **类装饰器无返回值**：若类装饰器无返回值或返回`undefined`，那被装饰的类**不会**被替换。
function Demo3(target:Function){
    return class {
        test(){
            console.log(200);
            console.log(300);
            console.log(400);
        }
    }
}

@Demo3 // 装饰 Person9

class Person9 { // Person9是被装饰的类
    test(){
        console.log(100);
    }
}
console.log('Person9',Person9);

type Constructor = new (...arg:any[]) => {}

// 替换被装饰的类


// T extends Constructor泛型约束
// 装饰器
// 不加这个p10.getTime()会报错
interface Person10 {
    getTime(): void
}
function LogTime<T extends Constructor>(target:T){
    return class extends target { // 此处替代Person10
        createdTime:Date
        constructor(...args:any[]){
            super(...args)
            this.createdTime = new Date()
        }
        getTime(){
            return `该对象的创建时间是:${this.createdTime}`
        }
    }
}
/*
@LogTime
class Person10 {
上面这一段等价于 LogTime(Person10)
*/ 
@LogTime
class Person10 {
    name: string
    age: number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
    speak(){
        console.log('你好呀');
    }
}

const p10 = new Person10('张三',19)
console.log('p10',p10);
console.log(p10.getTime());

// 类Person11的简写形式
class Person11 {
    // name: string
    // age:number
    // constructor(name:string,age:number){
    //     this.name = name
    //     this.age = age
    // }

    constructor(
        public name: string,
        public age: number
    ){}
}


// 装饰器工厂
interface Person12 {
    introduce: ()=>void
}
function LogInfo2(n:number){
    // 下面的是装饰器
    return function(target:Function){
        target.prototype.introduce = function(){
            for(let i = 0; i < n; i++){
                console.log(`我的名字:${this.name},我的年龄:${this.age}岁`);
                
            }
        }
    }
}
@LogInfo2(3)
class Person12 {
    constructor(
        public name: string,
        public age: number
    ) {}
    speak(){
        console.log('你好呀');
    }
}

const p12 = new Person12('刘惠杰',18)
p12.introduce() 


/* 
参数说明：
    ○ target: 对于静态属性来说值是类，对于实例属性来说值是类的原型对象。
    ○ propertyKey: 属性名。
*/
// 属性装饰器
function Demo(target:object,propertykey:string){
    console.log(target,propertykey); // target-{}--(就是Person13), propertykey--name
}

class Person13 {
    @Demo name:string
    @Demo age: number
    static school:string
    constructor(name: string,age:number){
        this.name = name
        this.age = age
    }
}

// 给指定对象添加属性用的
let value = 130
Object.defineProperty(Person13.prototype,'age',{
    get(){
        return value
    },
    set(val){
        value = val
    },
})
const p13 = new Person13('你好',17)
console.log(p13);

// 方法装饰器
function Demo5(target:any,propertyKey:any,description:any){
    console.log(target);
    console.log(propertyKey);
    console.log(description);
}

class Person14{
    constructor(
        public name:string,
        public age:number,
    ){}

    @Demo5 // 放在谁上面就装饰谁
    speak(){
        console.log(`你好我的名字:${this.name},今年${this.age}岁`);
    }
    static isAdult(age:number){
        return age >= 18
    }
}




